using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;

namespace RF_Jammer_Base
{
    class TargetNode
    {
        const double SPEED_OF_LIGHT = 2.99792458E8;

        private int id;
        private double xCoordinate;
        private double yCoordinate;
        private double jammingPower;
        private double frequency;

        private double incidentPower;
        private double localPhase;

        /* public constructor for a new target node */
        public TargetNode(int id, double xCoordinate, double yCoordinate, double jammingPower, double frequency)
        {
            this.id = id;
            this.frequency = frequency;
            this.xCoordinate = xCoordinate;
            this.yCoordinate = yCoordinate;
            this.jammingPower = jammingPower;

            TargetNode.writeOut("New " + this.ToString(), Jammer.NOTICE);
        }

        public void CalcIncidentPower(ArrayList SourceNodes)
        {
            double distance = 0;
            double pathloss = 0;
            double receivedpower = 0;
            double phaseoffset = 0;

            this.incidentPower = 0;
            this.localPhase = 0;

            foreach (SourceNode sNode in SourceNodes)
            {
                /* determine if selected source node is aimed at this target */
                if( sNode.CheckAim(xCoordinate,yCoordinate) )
                {
                    /* calculate distance between source and target */
                    distance = CalcDistance(xCoordinate, yCoordinate, sNode.XCoordinate, sNode.YCoordinate);

                    /* calculate path loses between source and target */
                    pathloss = CalcPathLoss(this.frequency, distance);
                    receivedpower = sNode.OutputPower/pathloss;

                    /* calculate phase offset at target */
                    phaseoffset = CalcPhaseOffset(this.frequency, distance);

                    /* add resultant power from this target to incedent power taking phase into account */
                    AddPhasors(this.incidentPower,
                               this.localPhase, 
                               receivedpower, 
                               phaseoffset,
                               ref this.incidentPower,
                               ref this.localPhase);
                }
            }
        }

        /* customized to string method for the target node object */
        public override string ToString()
        {
            string str = "";
            str += string.Format("Target Node {0}, ", id); 
            str += string.Format("Location: {0:F},{1:F}, ", xCoordinate, yCoordinate);
            str += string.Format("Jamming Power: {0:F}, ", jammingPower);
            str += string.Format("Incident Power: {0:F}", incidentPower);
            return str; 
        }

        /* customized debug output method for the target node object */
        private static void writeOut(string str, int level)
        {
            str = string.Format("Target Node {0} - ", str);
            Jammer.DebugOut(str, level);
        }

        static double CalcPhaseOffset(double frequency, double distance)
        {
            /* Calcuate wavelength */
            double wavelength = SPEED_OF_LIGHT / frequency;

            /* Calcuate wave length relative offset */
            double mod = distance % wavelength;

            /* Calcuate resultant phase offset */
            double phaseOffset = mod / wavelength * 2 * Math.PI;
            double phaseOffsetDeg = phaseOffset * (180 / Math.PI);

            TargetNode.writeOut(string.Format("Wavelength = {0:F}m", wavelength),Jammer.DEBUG);
            TargetNode.writeOut(string.Format("Modulus = {0:F}", mod),Jammer.DEBUG);
            TargetNode.writeOut(string.Format("Phase offset = {0:F}rad, {0:F}deg", phaseOffset, phaseOffsetDeg),Jammer.DEBUG);

            return phaseOffsetDeg;
        }

        static double CalcDistance(double x1, double y1, double x2, double y2)
        {
            /* use pathagorean formula to calc distance between two points */
            double distance = Math.Sqrt(Math.Pow(x1 - x2, 2) + Math.Pow(y1 - y2, 2));
            return distance;
        }

        static double CalcPathLoss(double frequency, double distance)
        {
            double loss = 0;
            // Free space path loss = ((4*Pi*F*D)/C)^2
            // F is the RF frequency expressed in hertz (Hz).
            // D is the distance between the transmitting and receiving antennas in meters (m).
            // C is the speed of light in meters per second (m/s).
            loss = Math.Pow((4 * Math.PI * distance * frequency) / SPEED_OF_LIGHT, 2);            
            return loss;
        }

        static void AddPhasors(
            double magA,
            double phaseA,
            double magB,
            double phaseB,
            ref double magC,
            ref double phaseC)
        {
            double Areal, Breal, Creal;
            double Aimg, Bimg, Cimg;

            TargetNode.writeOut(string.Format("Phasor A = {0:F}e^j({1:F})", magA, phaseA), Jammer.DEBUG);
            TargetNode.writeOut(string.Format("Phasor B = {0:F}e^j({1:F})", magB, phaseB), Jammer.DEBUG);

            /* convert phase from degrees to radians */
            phaseA = phaseA * (Math.PI / 180);
            phaseB = phaseB * (Math.PI / 180);

            /* find complex components of phasor A */
            Areal = magA * Math.Cos(phaseA);
            Aimg = magA * Math.Sin(phaseA);

            /* find complex components of phasor B */
            Breal = magB * Math.Cos(phaseB);
            Bimg = magB * Math.Sin(phaseB);

            /* add complex components of phasor A and B to get C */
            Creal = Areal + Breal;
            Cimg = Aimg + Bimg;

            /* convert C back into polar form */
            magC = Math.Sqrt(Math.Pow(Creal, 2) + Math.Pow(Cimg, 2));
            if (Creal > 0)
            {
                phaseC = Math.Atan(Cimg / Creal);
            }
            else
            {
                phaseC = Math.PI - Math.Atan(Cimg / (-Creal));
            }

            /* convert to degrees */
            phaseC = phaseC * (180 / Math.PI);

            TargetNode.writeOut(string.Format("Phasor C = {0:F}e^j({1:F})", magC, phaseC), Jammer.DEBUG);
        }

        /* public getter and setter for id property of target node */
        public int ID
        {
            get
            {
                return id;
            }
            set
            {
                id = value;
            }
        }

        /* public getter and setter for x coordinate property of target node */
        public double XCoordinate
        {
            get
            {
                return xCoordinate;
            }
            set
            {
                xCoordinate = value;
            }
        }

        /* public getter and setter for y coordinate property of target node */
        public double YCoordinate
        {
            get
            {
                return yCoordinate;
            }
            set
            {
                yCoordinate = value;
            }
        }

        /* public getter and setter for jamming power property of target node */
        public double JammingPower
        {
            get
            {
                return jammingPower;
            }
            set
            {
                jammingPower = value;
            }
        }

        /* public getter and setter for incindent power property of target node */
        public double IncidentPower
        {
            get
            {
                return incidentPower;
            }
            set
            {
                incidentPower = value;
            }
        }
    }
}
